package com.ccf.business.knowledge.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ccf.business.knowledge.model.vo.LabelContentVO;
import com.ccf.business.knowledge.model.vo.LabelDataVO;
import com.ccf.business.knowledge.model.vo.LabelEntityVO;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.common.utils.constant.Constants;
import com.common.utils.constant.FileType;
import com.ccf.business.knowledge.mapper.LabelDataEntityMapper;
import com.ccf.business.knowledge.model.LabelDataEntity;
import com.ccf.business.knowledge.service.ILabelDataEntityService;
import com.common.link.cache.AtlasCacheService;
import com.common.link.model.EntityVO;
import com.common.model.pojo.Query;
import com.common.model.result.CodeEnum;
import com.common.model.result.PageResult;
import com.service.mybatis.service.impl.SuperServiceImpl;
import com.service.mybatis.util.Condition;
import com.framework.minio.service.MinioService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 标注任务关系标记
 *
 * @author ccf
 * @date 2022-02-15 16:56:10
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LabelDataEntityServiceImpl extends SuperServiceImpl<LabelDataEntityMapper, LabelDataEntity> implements ILabelDataEntityService {

    private final AtlasCacheService atlasCacheService;

    private final MinioService minioService;

    /**
    * 列表
    * @param query
    * @return
    */
    @Override
    public PageResult<LabelDataEntity> findList(Query query){
        Page<LabelDataEntity> page = page(Condition.getPage(query), Condition.getQueryWrapper(query));
        return PageResult.<LabelDataEntity>builder().datas(page.getRecords()).code(CodeEnum.SUCCESS.getCode()).count(page.getTotal()).build();
    }

    @Override
    public List<LabelEntityVO> entityList(String dataId) {
        List<LabelDataEntity> entities = list(Wrappers.<LabelDataEntity>query()
                        .lambda().eq(LabelDataEntity::getDataId, dataId));
        Map<String, List<LabelDataEntity>> entityMap =
                entities.stream().collect(Collectors.groupingBy(LabelDataEntity::getTypeId));
        List<LabelEntityVO> entityList = Lists.newArrayList();
        for (Map.Entry<String, List<LabelDataEntity>> entry: entityMap.entrySet()) {
            String key = entry.getKey();
            List<LabelDataEntity> value = entry.getValue();
            LabelEntityVO labelEntityVO = new LabelEntityVO();
            labelEntityVO.setId(key);
            EntityVO entityById = atlasCacheService.getEntityById(key);
            labelEntityVO.setName(Optional.ofNullable(entityById).map(EntityVO::getName).orElse(null));
            labelEntityVO.setColor(Optional.ofNullable(entityById).map(EntityVO::getColor).orElse(null));
            labelEntityVO.setEntityList(value.stream().map(e->
                    new LabelContentVO(e.getId(), e.getContext(), e.getStartOffset(), e.getEndOffset()))
                    .collect(Collectors.toList()));
            entityList.add(labelEntityVO);
        }
        return entityList;
    }

    @Override
    public void saveOrUpdateBatch(LabelDataVO labelData) {
        String id = labelData.getId();
        List<LabelEntityVO> entityTypeList = labelData.getEntityTypeList();
        List<LabelDataEntity> entities = Lists.newArrayList();
        for (LabelEntityVO labelEntityVO : entityTypeList) {
            String typeId = labelEntityVO.getId();
            List<LabelContentVO> entityList = labelEntityVO.getEntityList();
            for (LabelContentVO labelContentVO : entityList) {
                LabelDataEntity labelDataEntity = new LabelDataEntity();
                labelDataEntity.setDataId(id);
                labelDataEntity.setContext(labelContentVO.getLabel());
                labelDataEntity.setTypeId(typeId);
                labelDataEntity.setId(labelContentVO.getRandomClass());
                labelDataEntity.setStartOffset(labelContentVO.getStart());
                labelDataEntity.setEndOffset(labelContentVO.getEnd());
                entities.add(labelDataEntity);
            }
        }
        List<LabelDataEntity> oldDatas = list(Wrappers.<LabelDataEntity>query()
                .lambda().eq(LabelDataEntity::getDataId, id));
        if (CollUtil.isNotEmpty(oldDatas)){
            removeByIds(oldDatas.stream().map(e->e.getId()).collect(Collectors.toList()));
        }
        if (entities.size() > 0){
            saveBatch(entities);
        }
    }

    @Override
    public String entityTidy(String dataId, String data) {
        List<LabelDataEntity> list = list(Wrappers.<LabelDataEntity>query()
                .lambda().eq(LabelDataEntity::getDataId, dataId));
        if (list.size() == 0){
            return null;
        }
        String bmeoStr = getBMEOStr(list, data);
        String path = saveToMinio(bmeoStr, LabelDataEntity.DEFAULT_BMEO_FOLDER, FileType.TXT, dataId);
        return path;
    }

    /**
     * 保存到minio
     * @param data
     * @param folder
     * @param type
     * @return
     */
    private String saveToMinio(String data, String folder, FileType type, String dataId) {
        InputStream inputStream = new ByteArrayInputStream(data.getBytes(Charsets.UTF_8));
        String path = minioService.getBucketName() + Constants.SLASH + folder + Constants.SLASH + dataId + Constants.DOT + type.getValue();
        minioService.coverFile(path, inputStream, type.getValue());
        return path;
    }


    /**
     * 创建BMEO字符串
     *
     * @param entities
     * @param context
     * @return
     */
    private String getBMEOStr(List<LabelDataEntity> entities, String context) {
        char[] chars = context.toCharArray();
        int length = chars.length;
        //标签数组
        String[] labels = new String[length];
        Arrays.fill(labels, "O");
        //这里去判断实体对不对的上太费时间 不判断 要相信前端
        for (LabelDataEntity entity : entities) {
            Integer xoffset = entity.getStartOffset();
            Integer yoffset = entity.getEndOffset();
            String name = entity.getContext();
            if (xoffset == null || xoffset < 0 || xoffset > length - 1 ||
                    StringUtils.isEmpty(name)) {
                continue;//实体失效
            }
            int nameLength = name.length();
            EntityVO entityType= atlasCacheService.getEntityById(entity.getTypeId());
            if (entityType == null){
                continue;
            }
            String typeName = entityType.getName();
            labels[xoffset-1] = "B-" + typeName;
            labels[xoffset - 2 + nameLength ] = "E-" + typeName;
            for (int i = 0; i < nameLength -2; i++) {
                labels[xoffset + i] = "M-" + typeName;
            }
        }
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            //空格特殊处理
            if (chars[i] == 32) {
                sb.append((char) 10);
            } else {
                sb.append(chars[i]);
                sb.append(" ");
                sb.append(labels[i]);
                sb.append((char) 10);
            }
        }
        return sb.toString();
    }
}
